package com.bytz.modules.cms.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.bytz.common.exception.BytzBootException;
import com.bytz.modules.cms.common.exception.TranDateException;
import com.bytz.modules.cms.common.service.CmsConfigService;
import com.bytz.modules.cms.order.entity.OrderPayDetail;
import com.bytz.modules.cms.order.model.PayNotifyResult;
import com.bytz.modules.cms.order.service.IOrderPayDetailService;
import com.bytz.modules.cms.order.service.IOrderPayService;
import com.bytz.modules.cms.order.service.IOrderPickUpDetailService;
import com.bytz.modules.cms.pay.chinapay.service.impl.ChinaPayServiceImpl;
import com.bytz.modules.cms.pay.model.QueryPayIn;
import com.bytz.modules.cms.pay.model.StartPayIn;
import com.bytz.modules.cms.pay.service.IPayOnlineService;
import com.bytz.modules.cms.pay.service.ISpecificPayOnlineService;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;

import static com.bytz.modules.cms.order.constant.OrderPayConstants.PayWay.CHINA_PAY;
import static com.bytz.modules.cms.order.model.PayResponseStatus.Failed;

/**
 * 在线支付服务
 * @author shanxuezhong
 */
@Service
@Slf4j
public class PayOnlineServiceImpl implements IPayOnlineService {
    @Autowired
    private IOrderPayDetailService orderPayDetailService;

    @Autowired
    private IOrderPayService orderPayService;

    @Autowired
    private ChinaPayServiceImpl chinaPayService;

    @Autowired
    private CmsConfigService configService;

    @Value("${spring.profiles.active:}")
    private String profile;


    @Override
    @Transactional(noRollbackFor = TranDateException.class, rollbackFor = Exception.class)
    public String startPayPage(StartPayIn startPayIn) {
        OrderPayDetail orderPayDetail = getOrderPayDetail(startPayIn.getPayDetailId());
        ISpecificPayOnlineService payService = getSpecificPayOnlineService(orderPayDetail.getPayWay());

        // 如果过期时间为空则证明是之前的数据，需要更新过期时间
        if (orderPayDetail.getExpirationTime() == null){
            LocalDateTime expirationTime = LocalDateTime.now().plusMinutes(configService.getGlobal().getPayTimeOut());
            orderPayDetail.setExpirationTime(expirationTime);
            // 将过期时间更新到数据库
            orderPayDetailService.lambdaUpdate()
                    .set(OrderPayDetail::getExpirationTime, expirationTime)
                    .eq(OrderPayDetail::getId, orderPayDetail.getId());
        }
        // 如果订单过期则抛出异常
        if (orderPayDetail.getExpirationTime().isBefore(LocalDateTime.now())){
            orderPayService.rollbackPay(orderPayDetail);
            throw new TranDateException("支付订单超时【当前日期（" + DateUtil.now() + "）已经超过过期时间（" + orderPayDetail.getExpirationTime().minusMinutes(1) + "）】");
        }

        return payService.startPayPage(orderPayDetail);
    }

    /**
     * 判断当前日期是否在tranDateStr的正负一天的范围
     * @param tranDateStr tranDateStr
     * @return 是否在范围(true 在，false 不在) true的话证明该tranDate可用 false证明tranDate不在正负一天的范围则不可用
     */
    private Boolean checkTranDate(String tranDateStr){
        if (StringUtils.isBlank(tranDateStr)) {
            return false;
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        LocalDate targetDate = LocalDate.parse(tranDateStr, formatter);
        Range<LocalDate> range = Range.closed(targetDate.minusDays(1), targetDate.plusDays(1));
        return range.contains(LocalDate.now());
    }



    @Override
    public void payFrontNotify(HttpServletRequest request, HttpServletResponse response, String payWay) {
        // 前端通知有些只是本次请求的结果，并不是支付的结果，仅用于本地调试时使用
        if (profile.contains("local")) {
            ISpecificPayOnlineService payService = getSpecificPayOnlineService(payWay);

            PayNotifyResult payNotifyResult = payService.parseFrontNotify(request);
            checkAndNotify(payNotifyResult);
        }
    }

    @Override
    public void payBackNotify(HttpServletRequest request, String payWay) {
        ISpecificPayOnlineService payService = getSpecificPayOnlineService(payWay);

        PayNotifyResult payNotifyResult = payService.parseBackNotify(request);
        checkAndNotify(payNotifyResult);
    }

    @Override
    public PayNotifyResult queryPayResult(QueryPayIn queryPayIn) {
        OrderPayDetail orderPayDetail = getOrderPayDetail(queryPayIn.getPayDetailId());
        ISpecificPayOnlineService payService = getSpecificPayOnlineService(orderPayDetail.getPayWay());

        PayNotifyResult payResult = payService.queryPayResult(orderPayDetail);
        if (BeanUtil.isEmpty(payResult.getPayResponse())) {
            throw new BytzBootException("查询出错了，请稍后重试");
        }
        // 判断当前日期是否在tranDateStr的正负一天的范围
        Boolean checkTranDate = checkTranDate(orderPayDetail.getTranDate());
        // 不在当前范围将相关数据置为失败
        if (!checkTranDate) {
            // tranDate过期并且BankInstNo为空则证明没有选择银行记录置为失败
            if (StringUtils.isBlank(payResult.getBankInstNo())){
                payResult.getPayResponse().setPayResponseStatus(Failed);
                DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                payResult.setCompleteDate(LocalDate.now().format(dateFormatter));
                payResult.setCompleteDateTime(LocalDateTime.now().format(dateTimeFormatter));
                String errorMessage = "系统校验tranDate【当前日期（" + DateUtil.now() + "）不在tranDate（" + orderPayDetail.getTranDate() + "）正负一天的范围】，并且没有选择银行";
                log.info("该笔订单tranDate超时，并且没有选择支付银行置为失败。orderPayDetailId：{}", orderPayDetail.getId());
                checkAndNotify(payResult);
                orderPayDetailService.logErrorMessage(orderPayDetail.getId(), errorMessage);
            }
        }
        return payResult;
    }

    private ISpecificPayOnlineService getSpecificPayOnlineService(String payWay) {
        if (CHINA_PAY.equals(payWay)) {
            return chinaPayService;
        }

        throw new BytzBootException("不支持的支付方式【" + payWay + "】");
    }

    /**
     * 获取并验证OrderPayDetail
     */
    private OrderPayDetail getOrderPayDetail(String payDetailId) {
        OrderPayDetail orderPayDetail = orderPayDetailService.getById(payDetailId);
        if (orderPayDetail == null) {
            throw new BytzBootException("找不到此支付明细");
        }

        return orderPayDetail;
    }

    /**
     * 检查找执行Notify
     */
    private void checkAndNotify(PayNotifyResult payNotifyResult) {
        try {
            if (payNotifyResult.getPayResponse() == null) {
                throw new BytzBootException("无法识别的响应码【" + payNotifyResult.getCode() + "】");
            }

            orderPayService.notify(payNotifyResult);
        } catch (Exception ex) {
            orderPayDetailService.logErrorMessage(payNotifyResult.getPayDetailId(), ex.getMessage());
            throw ex;
        }
    }
}